#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/fs.h>

//#include <linux/init.h>
//#include <linux/delay.h>
//#include <asm/hardware.h>
//#include <asm/irq.h>
//#include <asm/arch/regs-gpio.h>

//设备类
static struct class *leds_class = NULL;
//子设备类
static struct class_device *leds_class_devs[4] = {NULL};
//主设备号
int major = 0;
//GPF 配置寄存器地址
volatile unsigned long *gpfconfig = NULL;
//GPF 数据寄存器
volatile unsigned long *gpfdate = NULL;

static int leds_open(struct inode *inode, struct file *file)
{
    int minor = MINOR(inode->i_rdev);

    //配置为输出引脚,gpf 4,5,6 引脚
    switch(minor)
    {
        case 0:
            *gpfconfig &= ~((0x3 << (4*2)) | (0x3 << (5*2)) | (0x3 << (6*2))); //清除对应位
            *gpfconfig |= ((0x1 << (4*2)) | (0x1 << (5*2)) | (0x1 << (6*2))); //将对应的位置1
            break;
        case 1:
            *gpfconfig &= ~(0x3 << (4*2)); //清除对应位
            *gpfconfig |= (0x1 << (4*2)); //将对应的位置1
            break;
        case 2:
        //配置为输出引脚,gpf 4,5,6 引脚
            *gpfconfig &= ~(0x3 << (5*2)); //清除对应位
            *gpfconfig |= (0x1 << (5*2)); //将对应的位置1
            break;
        case 3:
            *gpfconfig &= ~(0x3 << (6*2)); //清除对应位
            *gpfconfig |= (0x1 << (6*2)); //将对应的位置1
            break;
        default:
            break;
    }

    return 0;
}

static void set_gpio(int gpio, int val)
{
    if(val)
    {
        //点亮
        *gpfdate &= ~(1 << gpio);
    }
    else
    {
        //熄灭
        *gpfdate |= (1 << gpio);
    }
}

/*
 * buf 传入数据的缓存地址
 * count 传入数据的长度
 */
static ssize_t leds_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    int val;
    int minor = MINOR(file->f_dentry->d_inode->i_rdev);

    //获取应用程序传入的值
    copy_from_user(&val, buf, 4);

    //将值传入用户空间
    //copy_to_user();

    switch(minor)
    {
        case 0:
            set_gpio(4,val);
            set_gpio(5,val);
            set_gpio(6,val);
            break;
        case 1:
            set_gpio(4,val);
            break;
        case 2:
            set_gpio(5,val);
            break;
        case 3:
            set_gpio(6,val);
            break;
        default:
            break;
    }

    return 0;
}

static struct file_operations leds_fops = {
    .owner = THIS_MODULE,
    .open = leds_open,
    .write = leds_write,
};

int leds_init(void)
{
    int i = 0;

    //注册驱动程序
    major = register_chrdev(0, "leds_dev", &leds_fops);

    //创建一个class
    leds_class = class_create(THIS_MODULE, "leds");
    if(IS_ERR(leds_class))
        return PTR_ERR(leds_class);

    leds_class_devs[0] = class_device_create(leds_class, NULL, MKDEV(major, 0), NULL, "leds");
    if(unlikely(IS_ERR(leds_class_devs[0])))
        return PTR_ERR(leds_class_devs[0]);

    //在类下面创建子设备
    for(i=1;i<4;i++)
    {
        leds_class_devs[i] = class_device_create(leds_class, NULL, MKDEV(major, i), NULL, "led%d",i);
        if(unlikely(IS_ERR(leds_class_devs[i])))
            return PTR_ERR(leds_class_devs[i]);
    }

    //获取gpf config寄存器所对应的虚拟地址,一个寄存器32位 = 4字节，4个寄存器 size = 4 × 4 = 16
    gpfconfig = (volatile unsigned long *)ioremap(0x56000050,16);
    //获取gpf date寄存器所对应的虚拟地址,指针类型为4字节，所以 +1 为偏移4字节
    gpfdate = gpfconfig + 1;

    return 0;
}

void leds_exit(void)
{
    int i = 0;

    //移除注册驱动程序
    unregister_chrdev(major, "leds_dev");

    //移除驱动程序类下面的设备
    for(i=0;i<4;i++)
    {
        if(leds_class_devs[i] != NULL)
        {
            class_device_unregister(leds_class_devs[i]);
            leds_class_devs[i] = NULL;
        }
    }


    //移除驱动程序相关类
    class_destroy(leds_class);

    //取消地址映射
    iounmap(gpfconfig);
}

module_init(leds_init); //添加入口函数
module_exit(leds_exit); //删除入口函数

MODULE_LICENSE("GPL");
